#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/sockios.h>
#include <linux/if_packet.h>
#include <linux/if_arp.h>
#include "arp.h"

#include "autoip.h"

extern int pack_sock_fd;
const int inaddr_broadcast = INADDR_BROADCAST;

void set_arp_msg(arpMessage * msg, const u_short opcode, const u_long tipaddr, const u_char * thaddr,
		 const u_long sipaddr, const u_char * shaddr)
{
	memset(msg, 0, sizeof(arpMessage));
	memcpy(msg->ethhdr.h_dest, MAC_BCAST_ADDR, 6);
	memcpy(msg->ethhdr.h_source, shaddr, ETH_ALEN);
	msg->ethhdr.h_proto = htons(ETHERTYPE_ARP);
	msg->htype = htons(ARPHRD_ETHER);
	msg->ptype = htons(ETHERTYPE_IP);
	msg->hlen = ETH_ALEN;
	msg->plen = 4;
	msg->operation = htons(opcode);

	memcpy(msg->sHaddr, shaddr, ETH_ALEN);

	msg->sInaddr[3] = sipaddr >> 24;	// note that sInaddr is stored in big endian form
	msg->sInaddr[2] = (sipaddr & 0x00ff0000) >> 16;
	msg->sInaddr[1] = (sipaddr & 0x0000ff00) >> 8;
	msg->sInaddr[0] = (sipaddr & 0x000000ff);

	msg->tInaddr[3] = tipaddr >> 24;	// so does tInaddr
	msg->tInaddr[2] = (tipaddr & 0x00ff0000) >> 16;
	msg->tInaddr[1] = (tipaddr & 0x0000ff00) >> 8;
	msg->tInaddr[0] = (tipaddr & 0x000000ff);

	if (opcode == ARPOP_REPLY) {
		memcpy(msg->tHaddr, thaddr, ETH_ALEN);
	}

	auto_ip_debug("Target IP addr is %u.%u.%u.%u \n", msg->tInaddr[0], msg->tInaddr[1], msg->tInaddr[2],
		      msg->tInaddr[3]);
	auto_ip_debug("Source IP addr is %u.%u.%u.%u \n", msg->sInaddr[0], msg->sInaddr[1], msg->sInaddr[2],
		      msg->sInaddr[3]);
	auto_ip_debug("Target hardware address is: %02X:%02X:%02X:%02X:%02X:%02X \n", msg->tHaddr[0], msg->tHaddr[1],
		      msg->tHaddr[2], msg->tHaddr[3], msg->tHaddr[4], msg->tHaddr[5]);
	auto_ip_debug("Source hardware address is: %02X:%02X:%02X:%02X:%02X:%02X \n", msg->sHaddr[0], msg->sHaddr[1],
		      msg->sHaddr[2], msg->sHaddr[3], msg->sHaddr[4], msg->sHaddr[5]);

}

int check_ip_used(const u_long ip_to_check, const u_char * my_hw_addr)
{
	int rc;
	int len;
	int addr_len;
	int send_counter;
	char tHaddr[ETH_ALEN];
	arpMessage ArpMsgSend, ArpMsgRecv;
	struct sockaddr addr;

	memset(tHaddr, 0, ETH_ALEN);
	len = sizeof(arpMessage);
	memset(&ArpMsgSend, 0, sizeof(arpMessage));
	set_arp_msg(&ArpMsgSend, ARPOP_REQUEST, ip_to_check, tHaddr, 0, my_hw_addr);
	memset(&addr, 0, sizeof(struct sockaddr));
	strcpy(addr.sa_data, "lan");

	send_counter = 0;
	while (1) {
		struct timeval last_send;
		struct timeval now;

		if (send_counter++ > 4) {
			auto_ip_debug("have tried 4 times and have nothing to receive.\n");
			return (0);
		}

		my_gettimeofday(&last_send);
		if (sendto(pack_sock_fd, &ArpMsgSend, len, 0, &addr, sizeof(struct sockaddr)) == -1) {
			auto_ip_debug("sendto(): err: %d,[%s]\n", errno, strerror(errno));
			return (-1);
		}

		for (my_gettimeofday(&now); time_diff(now, last_send) < 50000; my_gettimeofday(&now)) {
			fd_set fs;
			struct timeval timeout;
			unsigned long usec = 50000 - time_diff(now, last_send);
			timeout.tv_sec = usec / 1000000;
			timeout.tv_usec = usec % 1000000;

			FD_ZERO(&fs);
			FD_SET(pack_sock_fd, &fs);

			rc = select(pack_sock_fd + 1, &fs, NULL, NULL, &timeout);
			if (rc <= 0) {
				continue;
			}

			if (FD_ISSET(pack_sock_fd, &fs)) {
				memset(&ArpMsgRecv, 0, sizeof(arpMessage));
				auto_ip_debug("I have something to read\n");
				addr_len = sizeof(struct sockaddr);
				if (recvfrom
				    (pack_sock_fd, &ArpMsgRecv, sizeof(arpMessage), 0, (struct sockaddr *)&addr,
				     &addr_len) == -1) {
					auto_ip_debug("recvfrom(): err: %d,[%s]\n", errno, strerror(errno));
					return (-1);
				}

				if (ArpMsgRecv.ethhdr.h_proto != htons(ETHERTYPE_ARP)) {
					continue;
				}

				if (ArpMsgRecv.operation == htons(ARPOP_REPLY)) {
					auto_ip_debug("ARPOP_REPLY received from %u.%u.%u.%u for %u.%u.%u.%u\n",
						      ArpMsgRecv.sInaddr[0], ArpMsgRecv.sInaddr[1],
						      ArpMsgRecv.sInaddr[2], ArpMsgRecv.sInaddr[3],
						      ArpMsgRecv.tInaddr[0], ArpMsgRecv.tInaddr[1],
						      ArpMsgRecv.tInaddr[2], ArpMsgRecv.tInaddr[3]);
				} else {
					continue;
				}

				if (memcmp(ArpMsgRecv.tHaddr, my_hw_addr, ETH_ALEN)) {
					auto_ip_debug
					    ("Target hardware address mismatch: %02X.%02X.%02X.%02X.%02X.%02X received, "
					     "%02X.%02X.%02X.%02X.%02X.%02X expected\n", ArpMsgRecv.tHaddr[0],
					     ArpMsgRecv.tHaddr[1], ArpMsgRecv.tHaddr[2], ArpMsgRecv.tHaddr[3],
					     ArpMsgRecv.tHaddr[4], ArpMsgRecv.tHaddr[5], my_hw_addr[0], my_hw_addr[1],
					     my_hw_addr[2], my_hw_addr[3], my_hw_addr[4], my_hw_addr[5]);
					continue;
				}

				if (memcmp(&ArpMsgRecv.sInaddr, &ip_to_check, 4)) {
					auto_ip_debug
					    ("Sender IP address mismatch: %u.%u.%u.%u received, %u.%u.%u.%u expected\n",
					     ArpMsgRecv.sInaddr[0], ArpMsgRecv.sInaddr[1], ArpMsgRecv.sInaddr[2],
					     ArpMsgRecv.sInaddr[3], ((unsigned char *)&ip_to_check)[0],
					     ((unsigned char *)&ip_to_check)[1], ((unsigned char *)&ip_to_check)[2],
					     ((unsigned char *)&ip_to_check)[3]);
					continue;
				}
				return 1;	//some has used this ip address
			}
		}
	}

	return (0);

}

/* send gratuitous arp to inform others */
int send_gra_arp(const u_long addr, const u_char * my_hw_addr)
{
	int len;
	struct sockaddr saddr;
	arpMessage graArpMsg;

	set_arp_msg(&graArpMsg, ARPOP_REPLY, addr, MAC_BCAST_ADDR, addr, my_hw_addr);
	len = sizeof(arpMessage);
	memset(&saddr, 0, sizeof(struct sockaddr));
	strcpy(saddr.sa_data, "lan");
	if (sendto(pack_sock_fd, &graArpMsg, len, 0, &saddr, sizeof(struct sockaddr)) == -1) {
		auto_ip_debug("send gratuitous arp sendto(): err: %d,[%s]\n", errno, strerror(errno));
		return (-1);
	}

	auto_ip_debug("I have already informed others my new IP\n");
	return (0);
}
